package model;

import interfaces.TaskI;
import interfaces.WorkerI;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;




public class Stage<IN,OUT, INOUT> extends Thread implements WorkerI<IN,OUT> //Callable<INOUT> 
{
	int idworker;
	//Task task;
	FIFOBuffer<INOUT> prev=null; 
	FIFOBuffer<INOUT> next=null;
	TaskI<IN,OUT> task=null;
	boolean dbg = false;
	//boolean dbg = true;
	
	
	/**
	 * This is the executing unit of a pipeline.
	 * This Stage is connected to a incoming and an outgoing Connector f.e. FIFOBuffer
	 * Can also so be a source or a drain of a pipline
	 * @param id - ID of the Worker
	 * @param p - incoming connector f.e. FIFOBuffer
	 * @param n - outgoing connector f.e. FIFOBuffer
	 * @param t - task to be executed
	 */
	public Stage(int id, FIFOBuffer<INOUT> p, FIFOBuffer<INOUT> n,
			TaskI<IN, OUT> t) {
		idworker = id;
		this.prev = p;
		this.next = n;
		this.task = t;
		if (dbg)
			System.err.println("\nWorker No: " + idworker);
		this.start();

	}

	@Override
	public void excute() throws Exception {
		this.run();

	}
	
	/* (non-Javadoc)
	 * @see java.lang.Thread#run()
	 */
	@Override
	public void run() {

		IN input=null;
		OUT output=null;

	    checkConnection();
	    
	    if (prev == null) {                        // SOURCE
	      output = task.process(null);
	      while (output != null) {
	        next.send((INOUT) output);
	        output = (OUT) task.process(null);
	      }
	      next.stop();  // end of processing
	    } else if (next == null) {                 // DRAIN
	      input = (IN) prev.recv();
	      while (! prev.eos()) {
	        task.process(input);
	        input = (IN) prev.recv();
	      }
	    } else {	
		
	    if (dbg) System.err.println("\nWorker "+this.idworker+"_"+this+" : prev="+prev+", next="+next);
	    while (! prev.eos()) {
	      input = (IN) prev.recv();
	      if (dbg) System.err.print("\nWorker "+this.idworker+"_"+this+": recv:"+input);
	      output = (OUT) task.process(input);
	      if (dbg) System.err.print("\nWorker "+this.idworker+"_"+this+" : processing "+input+" -> "+output);
	      next.send((INOUT) output);
	    }
	    next.stop();
	    if (dbg) System.err.println("\nWorker "+this.idworker+"_"+this+" finished");
	    }
	    
	}

	/**
	 * checks if anything is connected to this stage
	 * 
	 */
	private void checkConnection() {
		if (this.prev == null && this.next == null) {
			System.err.println("Processing failed, no input/output connected!");

		}
	}
	
	/**
	 * Returns the current task set in this stage
	 * 
	 * @return Task
	 */
	public TaskI<IN, OUT> currentTask() {
		return task;
	}
    
	/**
	 * Checks if the stage is alive
	 * 
	 * @return boolean
	 */
	public boolean isStageAlive() {
		return this.isAlive();
	}

	/**
	 * friendly stopping of this stage
	 */
	public void stopStage() {
		this.interrupt();
	}

	/**
	 * Sets the task t to this stage
	 * 
	 * @param t
	 */
	public void setTask(TaskI t) {
		this.task = t;
	}

	/**
	 * Sets connection to the previous stage
	 * 
	 * @param prev
	 */
	public void setConnectionFrom(FIFOBuffer<INOUT> prev) {
		this.prev = prev;
	}

	/**
	 * Sets connection to the next stage
	 * 
	 * @param next
	 */
	public void setConnectionTo(FIFOBuffer<INOUT> next) {
		this.next = next;
	}

}
